﻿using System.Collections.Generic;
using System.Linq;
using Pathfinding;
using QFramework;
using UnityEngine;

namespace Gp.Scripts.Core
{
    public class BattleCellGrid
    {
        public class BattleTraversalProvider : ITraversalProvider
        {
            private readonly BaseUnit _unit;
            private readonly BattleCellGrid _cellGrid;

            public BattleTraversalProvider(BaseUnit unit, BattleCellGrid cellGrid)
            {
                _unit     = unit;
                _cellGrid = cellGrid;
            }

            public bool CanTraverse(Path path, GraphNode node)
            {
                // //TODO YHK: 判断障碍
                var unit = _cellGrid.GetUnitByCell(new VBattleCell(node));
                if (unit == _unit) return true;
                return unit == null;
            }

            public uint GetTraversalCost(Path path, GraphNode node)
            {
                return 1;
            }
        }


        public GameObject CellGridRoot { get; private set; }
        public GameObject GridParent { get; private set; }

        private Dictionary<string, VBattleCell> _unitPosMap;
        // private Dictionary<VBattleCell, BaseUnit> _unitCellMap;

        private EventHandler _eventHandler = new();

        public void InitGrid(Transform battleRoot)
        {
            CellGridRoot = battleRoot.GetOrCreate<GameObject>($"{nameof(BattleCellGrid)} (class)");
            GridParent   = CellGridRoot.GetOrCreate<GameObject>("GridParent");
            _unitPosMap  = new Dictionary<string, VBattleCell>();
        }

        public void OnStartBattle()
        {
            _eventHandler.RegisterEvent<Event_Unit.Death>(args => _unitPosMap.Remove(args.Unit.Guid));
            _eventHandler.RegisterEvent<Event_Unit.Spawn>(args => OnUnitSpawn(args.Unit));
            _eventHandler.RegisterEvent<Event_Unit.Move>(OnUnitMove);

            Global.Get<BattleSystem>().BattleSettle.GetAllUnits().ForEach(OnUnitSpawn);
        }

        private void OnUnitMove(Event_Unit.Move args)
        {
            _unitPosMap[args.Unit.Guid] = args.DestinationCell;
        }

        private void OnUnitSpawn(BaseUnit unit)
        {
            NNInfo node = AstarPath.active.GetNearest(unit.transform.position);
            unit.transform.position = (Vector3) node.node.position;
            _unitPosMap[unit.Guid]  = new VBattleCell(node.node);
        }


        public void OnBattleEnd()
        {
            _eventHandler.UnRegisterAll();
        }

        /// <summary>
        /// 构造寻路路径，但需要调用CalculatePath 进行计算
        /// </summary>
        public CellGridPath ConstructPath(BaseUnit unit, VBattleCell destination)
        {
            var path = ABPath.Construct(GetCellOfUnit(unit).Position, destination.Position);
            var ret  = new CellGridPath(path);
            path.traversalProvider = new BattleTraversalProvider(unit, this);
            return ret;
        }

        public BaseUnit GetUnitByCell(VBattleCell cell)
        {
            string guid = null;
            foreach (var (key, value) in _unitPosMap)
            {
                if (value.Node != cell.Node) continue;
                guid = key;
                break;
            }

            return Global.Get<BattleSystem>().BattleSettle.GetUnitByGuid(guid);
        }

        public VBattleCell GetCellOfUnit(BaseUnit unit)
        {
            return _unitPosMap[unit.Guid];
        }

        public VBattleCell GetNearest(Vector3 pos)
        {
            return new VBattleCell(AstarPath.active.GetNearest(pos).node);
        }

        // public VBattleCell GetByIndex(VBattleCell cell, Vector2Int offset)
        // {
        //     (cell.Node.Graph as GridGraph).GetNode()
        // }

        public List<VBattleCell> GetRangeByRadius(VBattleCell source, int radius)
        {
            var constantPath = ConstantPath.Construct(source.Position, radius * 1000);
            AstarPath.StartPath(constantPath);
            constantPath.BlockUntilCalculated();
            return constantPath.allNodes.Select(x => new VBattleCell(x)).ToList();
        }

        #if UNITY_EDITOR
        public void OnDrawGizmos()
        {
            if (_unitPosMap == null) return;

            Gizmos.color = Color.red;
            foreach (var (key, cell) in _unitPosMap)
            {
                Gizmos.DrawCube(cell.Position, new Vector3(.3f, .3f, .3f));
            }
        }
        #endif
    }
}